import json
import re

from django import http
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.db import DatabaseError
from django.http import JsonResponse, HttpResponse, HttpResponseForbidden
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection

from celery_tasks.send_email.tasks import celery_send_active_email
from goods.models import SKU
from meiduo.utils.mei_duo import TimeOp, LoginRequiredMixin, \
    LoginRequiredJSONMixin
from .utils import *
from .models import User, Address
from .constants import *


# users/views


class Register(View):
    """注册"""

    def get(self, request):
        """注册页面"""
        return render(request, "register.html")

    def post(self, request):
        """注册新用户"""

        # print(request.POST)
        username = request.POST.get('username', None)
        mobile = request.POST.get('mobile', None)
        password = request.POST.get('password', None)
        password2 = request.POST.get('password2', None)
        allow = request.POST.get('allow', None)
        sms_code = request.POST.get('sms_code', None)

        if not all([username, mobile, password, password2, allow, sms_code]):
            # 有字段为空
            return render(request, 'register.html', {'error': '缺少必传参数'})
            # return http.HttpResponseForbidden('{"error": "缺少必传参数"}')

        # 校验字段格式
        if allow != 'on':
            return render(request, 'register.html', {'error': '请同意用户协议'})
            # return http.HttpResponseForbidden('{"error": "请同意用户协议"}')
        error = check(username, mobile, password)
        if error.get('username'):
            return render(request, 'register.html',
                          {'error': error['username']})
        if error.get('mobile'):
            return render(request, 'register.html', {'error': error['mobile']})
        if error.get('password'):
            return render(request, 'register.html',
                          {'error': error['password']})
        if password != password2:
            return render(request, 'register.html', {'error': '两次输入的密码不一致'})

        # 校验用户名、手机号和短信验证码
        # if User.objects.filter(username=username).count():
        #     return render(request, 'register.html', {'error': '用户名已存在'})
        #
        # if User.objects.filter(mobile=mobile).count():
        #     return render(request, 'register.html', {'error': '手机号已注册'})

        if query_db(User, username=username)['count']:
            return render(request, 'register.html', {'error': '用户名已存在'})
        if query_db(User, mobile=mobile)['count']:
            return render(request, 'register.html', {'error': '手机号已注册'})

        redis_conn = get_redis_connection('verify')
        check_code = redis_conn.get('sms_%s' % mobile)

        if not check_code:
            return render(request, 'register.html', {'error': '短信验证码已失效'})
            # return http.HttpResponseForbidden('{"error":"短信验证码已失效"}')

        if sms_code != check_code.decode():
            return render(request, 'register.html', {'error': '短信验证码错误'})

        # 保存
        try:
            user = User.objects.create_user(username=username,
                                            password=password, mobile=mobile)
        except DatabaseError:
            logger.error('手机号：[%s] 用户名：[%s] 注册失败' % (mobile, username))
            return render(request, 'register.html', {'error': '注册失败'})

        logger.info('手机号：[%s] 用户名：[%s] 注册成功' % (mobile, username))
        # 登录
        login(request, user)
        # 跳转首页
        response = redirect(reverse('content:index'))
        # 设置cookies,有效期为两周
        response.set_cookie('username', user.username, max_age=STATE_KEEP_TIME)
        return response


class Login(View):
    """
    用户登录
    """

    def get(self, request):
        """登录页面"""
        return render(request, 'login.html')

    def post(self, request):
        """登录"""

        # 获取登录信息
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        remembered = request.POST.get('remembered', None)

        # 检查参数完整
        if not all([username, password]):
            return render(request, 'login.html', {'account_errmsg': '缺少必填项'})

        # 检查格式、查询数据库
        if re.match(fusername, username):
            user = User.objects.filter(username=username)
        elif re.match(fmobile, username):
            user = User.objects.filter(mobile=username)
        else:
            return render(request, 'login.html', {'account_errmsg': '非法账号名'})

        # 检查账号身份
        if not user:
            return render(request, 'login.html', {'account_errmsg': '账号不存在'})
        # 查询账号状态
        user = user[0]
        hlock = UserLock('login')
        hlock.get_user_state(user.mobile)
        # 拦截冻结账号
        if hlock.frozen:
            return render(request, 'login.html',
                          {'account_errmsg': hlock.info})

        # 检查密码
        # 密码有误，增加一次密码错误次数，锁定账号，并获取锁定信息，响应登录页面.
        if not user.check_password(password):
            hlock.count += 1
            hlock.set_count()
            hlock.get_lock_info()
            return render(request, 'login.html',
                          {'account_errmsg': hlock.info})
        # 解除账号锁定
        hlock.unlock()

        # 旧方式
        # redis_conn = get_redis_connection('login')
        # count = redis_conn.get('lock_%s' % user.mobile)
        # # 获取账号状态
        # if count:   # 锁定、冻结
        #     count = int(count.decode())
        # else:       # 正常
        #     count = 0
        # # 拦截冻结账号
        # if count >= SECRET_ERROR_CHANCE:
        #     time = redis_conn.ttl('lock_%s' % user.mobile)
        #     content = {
        #         'account_errmsg':
        #             '账号已冻结，请联系后台解锁。或等待%s小时后重试' % TimeOp.s2hms(time)
        #     }
        #     return render(request, 'login.html', content)
        #
        # # 检查密码
        # # 密码有误，锁定账号，增加一次密码错误次数，响应登录页面，密码错误和剩余机会
        # if not user.check_password(password):
        #     redis_conn.set('lock_%s' % user.mobile, count + 1, LOGIN_LOCK_TIME)
        #     if SECRET_ERROR_CHANCE - count - 1 > 0:  # 0次机会时，锁定账号
        #         content = {
        #             'account_errmsg':
        #                 '密码错误, 你还有%s次机会' % (SECRET_ERROR_CHANCE - count - 1)
        #         }
        #     else:
        #         time = redis_conn.ttl('lock_%s' % user.mobile)
        #         content = {
        #             'account_errmsg':
        #                 '账号已冻结，请联系后台解锁。或等待%s小时后重试' % TimeOp.s2hms(time)
        #         }
        #     return render(request, 'login.html', content)
        #
        # # 解除账号锁定
        # redis_conn.delete('lock_%s' % user.mobile)

        # 登录、设置登录状态
        # 记住登录，保持长期登录状态。反之，临时登录状态，关闭浏览器时结束

        login(request, user)
        if remembered != 'on':
            request.session.set_expiry(0)  # 单位为秒
            cookie_expire = None
        else:
            request.session.set_expiry(STATE_KEEP_TIME)
            cookie_expire = STATE_KEEP_TIME

        # 响应
        # 回到登录之前的页面，或首页
        url = request.GET.get('next', reverse('content:index'))
        # url = request.GET.get('next')
        # if url:
        #     print('login-next:', url)
        # else:
        #     print('login-next:', url)
        #     url = reverse('content:index')

        response = redirect(url)

        # 设置cookies,有效期为两周
        response.set_cookie('username', user.username, max_age=cookie_expire)

        return response


# 重写as_view方法的两种途径: 扩展、继承
# class LoginRequired(object):
class LoginRequired(View):
    """
    验证用户是否登录
    """

    @classmethod
    def as_view(cls, **initkwargs):
        return login_required(super().as_view())


# class UserCenter(LoginRequired, View):
# class UserCenter(LoginRequired):
class UserCenter(LoginRequiredMixin, View):
    """
    用户中心
    """

    def get(self, request):
        """响应用户中心或登录页面"""
        data = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active,
        }
        return render(request, 'user_center_info.html', data)
        # return render(request, 'user_center_info.html')

        # print(request.user)
        # if request.user.is_authenticated:
        #     return render(request, 'user_center_info.html')
        # else:
        #     return redirect(reverse('users:login'))


class EmailActive(View):
    """
    激活邮箱
    """

    def get(self, request):
        """
        激活邮箱
        """
        token = request.GET.get('token')
        if not token:
            return JsonResponse({'errmsg': '缺少token'},
                                json_dumps_params={"ensure_ascii": False})
        user = check_email_token(token)
        if not user:
            return JsonResponse({'errmsg': 'token无效'},
                                json_dumps_params={"ensure_ascii": False})
        user = user[0]
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error('【邮箱激活】用户名:【%s】手机:【%s】错误[%s]' %
                         (user.username, user.mobile, e))
            return HttpResponse('<h1>邮箱激活失败<h1>')
        return HttpResponse('<h1>邮箱激活成功<h1><p>'
                            '<a href="%s">点击回到用户中心</a></p>'
                            % reverse('users:user_center'))

    def put(self, request):
        """
        保存邮箱
        """
        if not request.user.is_authenticated:
            return JsonResponse({'code': 4101, 'errmsg': '请先登录'})

        try:
            jsonstr = request.body.decode()
            data = json.loads(jsonstr)
        except:
            return JsonResponse({'code': 4102, 'errmsg': '请提交正确的数据'})

        if not isinstance(data, dict):
            return JsonResponse({'code': 4102, 'errmsg': '请提交正确的数据'})

        email = data.get('email')
        error = check(email=email)
        if error:
            return JsonResponse({'code': 4103, 'errmsg': error['email']})
        user = request.user
        try:
            user.email = email
            user.save()
        except Exception as e:
            logger.error('用户名:[%s]手机号:[%s]错误:[%s] [保存邮箱] 失败' %
                         (user.username, user.mobile, e))
            return JsonResponse({'code': 4104, 'errmsg': '数据库错误，邮箱保存失败'})

        # 生成邮箱激活链接
        url = generate_email_url(user)
        # print(url)
        # 发送激活邮件
        celery_send_active_email.delay(user.email, url)

        return JsonResponse({'code': 0, 'errmsg': '邮箱保存成功'})


class UserAddress(LoginRequiredMixin, View):
    """收货地址"""
    def get(self, request):
        """
        展示收货地址
        :return: json 所有收货地址列表
        """
        user = request.user
        addr = user.default_address
        default_address_id = addr.id if addr else 'null'
        # print(default_address_id)
        address_list =[]
        addresses = user.address.filter(is_delete=False)

        for address in addresses:
            address_dict = {
                'id': address.id,
                'title': address.title,
                'consignee': address.consignee,
                'province': address.province.name,
                'city': address.city.name,
                'area': address.area.name,
                'place': address.place,
                'mobile': address.mobile,
                'email': address.email,
                'tel': address.email
            }
            address_list.append(address_dict)

        print(address_list)
        context = {'default_address_id': default_address_id,
                   'address': address_list}
        return render(request, 'user_center_site.html', context)

    def put(self, request, id):
        """
        设置用户默认收货地址
        :return:
        """
        try:
            addr = Address.objects.filter(id=id)
            if not addr:
                return JsonResponse({'code': 4102, 'errmsg': '无效的地址id'})
            request.user.default_address = addr[0]
            request.user.save()
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [设置默认收货地址] 失败' % (e, request.user.mobile))
            return JsonResponse({'code': 4102, 'errmsg': '设置失败'})

        return JsonResponse({'code': 0, 'errmsg': '设置成功'})


class AddressManager(LoginRequiredMixin, View):
    """收货地址管理"""
    def get(self, request, id):
        """
        修改地址标题
        :param id: 地址 id
        :return: json 地址信息
        """
        title = request.GET.get('title')
        if not title:
            return JsonResponse({'code': 4102, 'errmsg': '缺少title'})
        if len(title) > 50:
            return JsonResponse({'code': 4102, 'errmsg': '标题长度不能超过50'})
        try:
            addr = Address.objects.filter(id=id)
            if not addr:
                return JsonResponse({'code': 4102, 'errmsg': '无效的地址id'})
            addr.update(title=title)
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [修改地址标题] 失败' % (e, request.user.mobile))
            return JsonResponse({'code': 4102, 'errmsg': '修改失败'})

        return JsonResponse({'code': 0, 'errmsg': '修改成功'})

    def post(self, request):
        """
        添加收件地址
        :param request: body 中包含json字典
        :return: 成功、非登录用户
        """
        # if not request.user.is_authenticated:
        #     return JsonResponse({'code': 4101, 'errmsg': '请先登录'})
        # 检查已有地址数量
        user = request.user
        if user.address.filter(is_delete=False).count() >= ADDRESS_COUNT_LIMIT:
            return JsonResponse({'code': 4102, 'errmsg': '收货地址数量已达上限'})

        # print(request.body.decode())
        """
        request.body.decode()
           {"receiver":"abc",
            "province_id":110000,
            "city_id":110100,
            "district_id":110101,
            "place":"龙沙街道34号",
            "mobile":"17369243721",
            "tel":"",
            "email":""}
        """
        address = json.loads(request.body.decode())
        # 校验参数
        consignee = address['consignee']
        mobile = address['mobile']
        province = address['province_id']
        city = address['city_id']
        area = address['district_id']
        place = address['place']
        tel = address['tel']
        email = address['email']

        # 校验参数
        if not all([consignee, province, city, area, place, mobile]):
            # print('缺少必传参数')
            return http.HttpResponseForbidden('缺少必传参数')
        error = check(mobile=mobile, email=email, tel=tel)
        if error:
            # print(error)
            return http.JsonResponse(error)

        # 保存数据
        try:
            maddress = Address.objects.create(title=consignee, user=user, place=place,
                                              consignee=consignee, mobile=mobile,
                                              province_id=province, city_id=city,
                                              area_id=area, tel=tel, email=email)
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [保存收货地址] 失败' % (e, user.mobile))
            return JsonResponse({'code': 4102, 'errmsg': '地址添加失败'})
        # 设置默认地址
        if not user.default_address:
            user.default_address = maddress
            user.save()

        address = {
            'id': maddress.id,
            'title': maddress.title,
            'consignee': maddress.consignee,
            'province': maddress.province.name,
            'city': maddress.city.name,
            'area': maddress.area.name,
            'place': maddress.place,
            'mobile': maddress.mobile,
            'email': maddress.email,
            'tel': maddress.email
        }

        # print('添加地址成功')
        return JsonResponse({'code': 0, 'errmsg': '添加地址成功', 'address': address})

    def put(self, request, id):
        """
        修改收货地址
        :param id: 地址 id
        :return: json 地址信息
        """
        address = json.loads(request.body.decode())
        # 校验参数
        consignee = address['consignee']
        mobile = address['mobile']
        province = address['province_id']
        city = address['city_id']
        area = address['district_id']
        place = address['place']
        tel = address['tel']
        email = address['email']

        # 校验参数
        if not all([consignee, province, city, area, place, mobile]):
            # print('缺少必传参数')
            return http.HttpResponseForbidden('缺少必传参数')
        error = check(mobile=mobile, email=email, tel=tel)
        if error:
            # print(error)
            return http.JsonResponse(error)

        # 更新数据
        try:
            addr = Address.objects.filter(id=id)
            if not addr:
                return JsonResponse({'code': 4102, 'errmsg': '无效的地址id'})
            addr.update(title=consignee, user=request.user, mobile=mobile,
                        consignee=consignee, place=place, province_id=province,
                        city_id=city, area_id=area, tel=tel, email=email)
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [地址修改失败] 失败' % (e, request.user.mobile))
            return JsonResponse({'code': 4102, 'errmsg': '地址修改失败'})

        addr = addr[0]
        address = {
            'id': addr.id,
            'title': addr.title,
            'consignee': addr.consignee,
            'province': addr.province.name,
            'city': addr.city.name,
            'area': addr.area.name,
            'place': addr.place,
            'mobile': addr.mobile,
            'email': addr.email,
            'tel': addr.email
        }

      # print('地址修改成功')
        return JsonResponse({'code': 0, 'errmsg': '地址修改成功', 'address': address})

    def delete(self, request, id):
        """
        删除收货地址
        :param id: 地址 id
        :return: json
        """
        try:
            addr = Address.objects.filter(id=id)
            if not addr:
                return JsonResponse({'code': 4102, 'errmsg': '无效的地址id'})
            addr.update(is_delete=True)
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [删除收货地址] 失败' % (e, request.user.mobile))
            return JsonResponse({'code': 4102, 'errmsg': '删除失败'})

        return JsonResponse({'code': 0, 'errmsg': '删除成功'})

class Logout(View):
    """
    退出登录
    """

    def get(self, request):
        # 退出登录
        logout(request)
        # 重定向到首页，删除cookies中的用户信息
        response = redirect(reverse('content:index'))
        response.delete_cookie('username')

        return response


class AlterPassword(LoginRequiredMixin, View):
    """
    修改密码
    """

    def get(self, request):
        """
        展示修改密码页面
        :return: 模板
        """
        return render(request, 'user_center_pass.html')

    def post(self, request):
        """
        修改密码
        :return: json 错误提示 退出登录，重定向到登录页面
        """
        old_pswd = request.POST.get('old_password')
        new_pswd = request.POST.get('new_password')
        new_pswd2 = request.POST.get('new_password2')
        if not all([old_pswd, new_pswd, new_pswd2]):
            return render(request, 'user_center_pass.html', {'change_password_errmsg': '缺少必传参数'})
        if check(password=old_pswd) or check(password=new_pswd):
          # print('密码格式不正确')
            return render(request, 'user_center_pass.html', {'origin_password_errmsg': '密码格式不正确'})
        if new_pswd != new_pswd2:
          # print('两次输入的密码不相同')
            return render(request, 'user_center_pass.html', {'change_password_errmsg': '两次输入的密码不相同'})
        # 检查密码
        user = request.user
        try:
            if not user.check_password(old_pswd):
                return render(request, 'user_center_pass.html',
                              {'origin_password_errmsg': '原密码错误'})
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [修改密码] 失败' % (e, user.mobile))
            return render(request, 'user_center_pass.html', {'change_password_errmsg': '非法操作'})
        # 修改密码
        try:
            user.set_password(new_pswd)
            user.save()
        except Exception as e:
            logger.error('[数据库错误] %s [手机号] %s [修改密码] 失败' % (e, user.mobile))
          # print('修改密码失败')
            return render(request, 'user_center_pass.html', {'change_password_errmsg': '修改密码失败'})
        # 清理状态保持信息
      # print('修改密码成功')
        logout(request)
        response = redirect(reverse('users:login'))
        response.delete_cookie('username')

        return response


def username_count(request, name):
    """检查用户名是否存在"""
    data = {
        'count': User.objects.filter(username=name).count()
    }
    # print(data['count'])

    if data['count']:
        data['code'] = 400
        data['errmsg'] = '用户名已存在'
    else:
        data['code'] = 200
        data['errmsg'] = '用户名有效'

    return JsonResponse(data, json_dumps_params={"ensure_ascii": False})


def mobile_count(request, mobile):
    """检查手机号是否存在"""
    data = {
        'count': User.objects.filter(mobile=mobile).count()
    }
    # print(data['count'])

    if data['count']:
        data['code'] = 400
        data['errmsg'] = '用户名已存在'
    else:
        data['code'] = 400
        data['errmsg'] = '用户名有效'

    return JsonResponse(data, json_dumps_params={"ensure_ascii": False})


class UserBrowseHistory(LoginRequiredJSONMixin, View):
    """用户浏览记录"""

    def post(self, reqeust):
        """保存用户商品浏览记录"""
        # 接收参数
        json_str = reqeust.body.decode()
        json_dict = json.loads(json_str)
        sku_id = json_dict.get('sku_id')
        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseForbidden('参数sku_id错误')
        # 保存sku_id到redis
        redis_conn = get_redis_connection('history')
        user = reqeust.user
        pl = redis_conn.pipeline()
        # 先去重
        pl.lrem('history_%s' % user.id, 0, sku_id)
        # 再保存：最近浏览的商品在最前面
        pl.lpush('history_%s' % user.id, sku_id)
        # 最后截取
        pl.ltrim('history_%s' % user.id, 0, 4)
        # 执行
        pl.execute()
        # 响应结果
        return JsonResponse({'code': 0, 'errmsg': 'OK'})

    def get(self, request):
        """查询用户商品浏览记录"""
        # 获取登录用户信息
        user = request.user
        # 创建连接到redis对象
        redis_conn = get_redis_connection('history')
        # 取出列表数据（核心代码）
        sku_ids = redis_conn.lrange('history_%s' % user.id, 0, -1)  # (0, 4)
        # 将模型转字典
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.filter(id=sku_id)
            if sku.count():
                sku = sku[0]
                skus.append({
                    'id': sku.id,
                    'name': sku.name,
                    'price': sku.price,
                    'default_image_url': sku.default_image.url
                })
        # print(len(skus))
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'skus': skus})


class OrderManager(LoginRequiredJSONMixin, View):
    """用户订单管理"""
    def get(self, request):

        data = {'orders': request.user.orderinfo_set.all()}
        return render(request, 'user_center_order.html', data)
